using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.GZip;
using UnityEngine;
using UnityEngine.Networking;
using LuaInterface;

namespace YanhuaMMO
{
    public static class Http
    {
        public static int httpReqTimeOut = 0;//0,默认时间20s;
        internal class SimpleCertificate : CertificateHandler
        {
            protected override bool ValidateCertificate(byte[] certificateData)
            {
                return true;
            }
        }

        private static readonly SimpleCertificate sSSLCert = new SimpleCertificate();

        public static void Get(String url, LuaFunction callback, LuaTable headers = null)
        {
            Get(url, null, callback, headers);
        }

        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="queryDict"></param>
        /// <param name="callback"></param>
        /// <param name="headers"></param>
        public static void Get(String url, LuaTable queryDict, LuaFunction callback, LuaTable headers = null)
        {
            var req = UnityWebRequest.Get(UrlAppendParams(url, LuaDictTableToDict(queryDict)));
            req.timeout = httpReqTimeOut;
            if(Application.platform != RuntimePlatform.IPhonePlayer
                && Application.platform != RuntimePlatform.OSXEditor
                && Application.platform != RuntimePlatform.OSXPlayer)
            {
                req.SetRequestHeader("Accept-Encoding", "gzip");
            }            
            SetRequestHeaders(req, headers, false);
            req.certificateHandler = sSSLCert;

            var asyncOp = req.SendWebRequest();

            asyncOp.completed += (AsyncOperation op) =>
            {
                try
                {
                    if (op.isDone)
                    {
                        if (req.isNetworkError)
                        {
                            callback.Call(false, String.Format("Request {0} failed. The network is not connectable: {0}", url, req.error));
                        }
                        else if (req.isHttpError)
                        {
                            callback.Call(false, String.Format("Request {0} failed. ErrCode: {1}. ErrMsg: {2}", url, req.responseCode, req.error));
                        }
                        else
                        {
                            var encoding = req.GetResponseHeader("Content-Encoding");
                            if(Application.platform == RuntimePlatform.IPhonePlayer 
                               || Application.platform == RuntimePlatform.OSXPlayer
                                || Application.platform == RuntimePlatform.OSXEditor)
                            {
                                encoding = "aa";
                            }
                            var data = req.downloadHandler.data;
                            if (data == null)
                            {
                                callback.Call(false, String.Format("Request {0} failed. ErrCode: {1}. ErrMsg: {2}", url, req.responseCode, req.error));
                                return;
                            }
                            if (encoding == "gzip")
                            {
                                using (var compressedStream = new MemoryStream(data))
                                {
                                    using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
                                    {
                                        using (var sr = new StreamReader(zipStream)) {
                                            callback.Call(true, sr.ReadToEnd());
                                        }
                                    }
                                }
                            }
                            else
                            {
                                callback.Call(true, req.downloadHandler.text);
                            }
                        }
                    }
                    else
                    {
                        // 请求未完成？
                        callback.Call(false, String.Format("Request has not been done: {0}", req.error));
                    }
                }
                finally
                {
                    httpReqTimeOut = 0;
                    req.Dispose();
                }                
            };
        }
        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <param name="callback"></param>
        /// <param name="headers"></param>
        public static void Post(String url, String postData, LuaFunction callback, LuaTable headers = null)
        {
            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(postData);
            var req = new UnityWebRequest(url, "POST") 
            {
                uploadHandler = new UploadHandlerRaw(bodyRaw),
                downloadHandler = new DownloadHandlerBuffer()
            };            

            req.certificateHandler = sSSLCert;            

            SetRequestHeaders(req, headers, true);

            var asyncOp = req.SendWebRequest();

            asyncOp.completed += (AsyncOperation op) =>
            {
                try
                {
                    if (op.isDone)
                    {
                        if (req.isNetworkError)
                        {
                            callback.Call(false, String.Format("Request has network error: {0}", req.error));
                        }
                        else if (req.isHttpError)
                        {
                            callback.Call(false, String.Format("Request {0} failed. ErrCode: {1}. ErrMsg: {2}", url, req.responseCode, req.error));
                        }
                        else
                        {
                            callback.Call(true, req.downloadHandler.text);
                        }
                    }
                    else
                    {
                        // 请求未完成？
                        callback.Call(false, String.Format("Request has not been done: {0}", req.error));
                    }
                }
                finally
                {
                    req.Dispose();
                }
            };
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <param name="callback"></param>
        /// <param name="headers"></param>
        public static void Post(String url, LuaTable postData, LuaFunction callback, LuaTable headers = null)
        {
            var req = UnityWebRequest.Post(url, LuaDictTableToDict(postData));

            req.certificateHandler = sSSLCert;

            SetRequestHeaders(req, headers, true);

            var asyncOp = req.SendWebRequest();

            asyncOp.completed += (AsyncOperation op) =>
            {
                try
                {
                    if (op.isDone)
                    {
                        if (req.isNetworkError)
                        {
                            callback.Call(false, String.Format("Request has network error: {0}", req.error));
                        }
                        else if (req.isHttpError)
                        {
                            callback.Call(false, String.Format("Request {0} failed. ErrCode: {1}. ErrMsg: {2}", url, req.responseCode, req.error));
                        }
                        else
                        {
                            callback.Call(true, req.downloadHandler.text);
                        }
                    }
                    else
                    {
                        // 请求未完成？
                        callback.Call(false, String.Format("Request has not been done: {0}", req.error));
                    }
                }
                finally
                {
                    req.Dispose();
                }
            };
        }

        private static void SetRequestHeaders(UnityWebRequest request, LuaTable headers, bool isPost)
        {
            if (headers != null)
            {
                bool emptyHeaders = true;
                var strType = typeof(String);
                foreach (var kvp in headers.ToDictTable())
                {
                    if (kvp.Key.GetType() == strType && kvp.Value.GetType() == strType)
                    {
                        request.SetRequestHeader((String)kvp.Key, (String)kvp.Value);
                        emptyHeaders = false;
                    }
                }
                if (emptyHeaders && isPost)
                {
                    request.SetRequestHeader("Content-type", "application/json;charset=utf-8");
                }
            }
            else
            {
                if (isPost)
                {
                    request.SetRequestHeader("Content-type", "application/json;charset=utf-8");
                }
            }
        }

        private static Dictionary<String, String> LuaDictTableToDict(LuaTable luaDictTable)
        {
            var ret = new Dictionary<String, String>();
            
            if (luaDictTable != null)
            {
                var strType = typeof(String);
                foreach (var kvp in luaDictTable.ToDictTable())
                {
                    if (kvp.Key.GetType() == strType && kvp.Value.GetType() == strType)
                    {
                        ret.Add((String)kvp.Key, (String)kvp.Value);
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// Url拼接参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="queryDict"></param>
        /// <returns></returns>
        private static string UrlAppendParams(String url, Dictionary<string, string> queryDict)
        {
            if (queryDict == null || queryDict.Count == 0)
            {
                return url;
            }

            var p = new ArrayList();
            foreach (var kvp in queryDict)
            {
                var k = kvp.Key;
                var v = kvp.Value;

                p.Add(UnityWebRequest.EscapeURL(k) + "=" + UnityWebRequest.EscapeURL(v));
            }

            if (p.Count <= 0)
            {
                return url;
            }

            url += (url.IndexOf("?") != -1 ? "&" : "?") + (string.Join("&", p.ToArray()));

            return url;
        }

        //public static void Test()
        //{
        //    var testUrl = "https://httpbin.org/";
        //    // Get
        //    Get(testUrl + "get?213", null, (bool succ, string ret) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Get success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Get failed: {0}", ret));
        //        }
        //    }, null);

        //    var paramDict = new Dictionary<string, string>
        //    {
        //        {"p1", "123"}
        //    };

        //    Get(testUrl + "get", paramDict, (bool succ, string ret) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Get with paramDict success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Get with paramDict failed: {0}", ret));
        //        }
        //    }, null);

        //    var headers = new Dictionary<string, string>
        //    {
        //        { "s", "s"}, { "s1", "s"}
        //    };

        //    // Get With custom header
        //    Get(testUrl + "get", null, (bool succ, string ret) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Get with headers success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Get with headers failed: {0}", ret));
        //        }
        //    }, headers);

        //    // Get Binary
        //    GetBinary(testUrl + "get", null, (bool succ, byte[] ret, string errMsg) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Get binary success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Get binary failed: {0}", ret));
        //        }
        //    }, null);

        //    // Get Binary With custom header
        //    GetBinary(testUrl + "get", null, (bool succ, byte[] ret, string errMsg) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Get binary with headers success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Get binary with headers failed: {0}", ret));
        //        }
        //    }, headers);

        //    // Post 
        //    Post(testUrl + "post", "Post Hello", (bool succ, string ret) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Post string success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Post string failed: {0}", ret));
        //        }
        //    }, headers);

        //    var postData = new Dictionary<string, string>
        //    {
        //        {"123", "345" }, {"1234", "78" }
        //    };

        //    // Post With custom header
        //    Post(testUrl + "post", postData, (bool succ, string ret) =>
        //    {
        //        if (succ)
        //        {
        //            Native.Log.DebugLog(String.Format("Post dict success: {0}", ret));
        //        }
        //        else
        //        {
        //            Native.Log.DebugLog(String.Format("Post dict failed: {0}", ret));
        //        }
        //    }, headers);
        //}
    }
}
